package linmath

import (
	"fmt"
	"math"
	"math/rand"
	"src/protoMsg"

	// "src/zeus/logger"
	"time"
)

// RandXZ 在XZ平面上半径为r的圆内选取一个随机点
func RandXZ(v Vector3, r float32) Vector3 {
	randSeed := rand.New(rand.NewSource(time.Now().UnixNano()))

	tarR := randSeed.Float64() * float64(r)
	angle := randSeed.Float64() * 2 * math.Pi

	pos := Vector3{}
	pos.Y = 0

	pos.X = float32(math.Cos(angle) * tarR)
	pos.Z = float32(math.Sin(angle) * tarR)

	return v.Add(pos)
}

// 角度进180度的表示
func AngleTo180(angle float32) float32 {
	for angle < 0 && angle >= 180 {
		if angle < 0 {
			angle += 180
		} else if angle >= 180 {
			angle -= 180
		}
	}
	return angle
}

// 角度转弧度
func AngleToRadian(angle float32) float32 {
	return (angle / 180.0) * math.Pi
}

// 弧度转角度
func RadianToAngle(radian float32) float32 {
	return (radian / math.Pi) * 180.0
}

// VectorToAngle 将一个向量转化成角度，Y轴为垂直轴
// 在使用这个函数的时候，无需对向量归一化
func VectorToAngle(v Vector3) float32 {
	return RadianToAngle(float32(math.Atan2(float64(v.Z), float64(v.X))))
}

// 对点进行平面移动
func PosMove(pos Vector3, rota int32, radius float32) Vector3 {
	forward := Rota2Vector(rota)
	forward.MulS(radius)
	return pos.Add(forward)
}

func PosMoveToProto(pos Vector3, rota int32, radius float32) *protoMsg.Vector3 {
	return PosToProto(PosMove(pos, rota, radius))
}

func PosToProto(pos Vector3) *protoMsg.Vector3 {
	return &pos.Vector3
}

// Rota2Vector rota转vector3
func Rota2Vector(rota int32) Vector3 {
	rota_radius := float64(rota) / 180 * math.Pi
	var SP float32 = 0
	var CP float32 = 1
	SY := float32(math.Sin(rota_radius))
	CY := float32(math.Cos(rota_radius))

	return Vector3{
		Vector3: protoMsg.Vector3{
			X: float32(CP * CY),
			Y: SP,
			Z: CP * SY,
		},
	}
}

// 向量转角度
func Vector2Rota(pos Vector3) int32 {
	angle := int32(RadianToAngle(float32(math.Atan(float64(pos.Z) / float64(pos.X)))))
	if pos.X < 0 {
		angle = 180 + angle
	}
	return angle
}

// 计算 |p1 p2| X |p1 p|
func GetCross(p1 Vector3, p2 Vector3, p Vector3) float32 {
	return (p2.X-p1.X)*(p.Z-p1.Z) - (p.X-p1.X)*(p2.Z-p1.Z)
}

// 判断圆心点p+半径是否在p1p2p3p4的正方形内
func IsCircleInMatrix(p1 Vector3, p2 Vector3, p3 Vector3, p4 Vector3, p Vector3, Radius float32) bool {
	// 检查线段两个端点是否在圆内
	distance1 := p1.Sub(p).LenPow2()
	distance2 := p2.Sub(p).LenPow2()
	distance3 := p3.Sub(p).LenPow2()
	distance4 := p4.Sub(p).LenPow2()
	circledist := Radius * Radius
	if distance1 <= circledist ||
		distance2 <= circledist ||
		distance3 <= circledist ||
		distance4 <= circledist {
		return true
	}

	if BetweenLineAndCircle2Num(p, Radius, p1, p2) {
		return true
	} else if BetweenLineAndCircle2Num(p, Radius, p2, p3) {
		return true
	} else if BetweenLineAndCircle2Num(p, Radius, p3, p4) {
		return true
	} else if BetweenLineAndCircle2Num(p, Radius, p4, p1) {
		return true
	}
	return false
}

// 判断点p是否在p1p2p3p4的正方形内
func IsPointInMatrix(p1 Vector3, p2 Vector3, p3 Vector3, p4 Vector3, p Vector3) bool {
	return GetCross(p1, p2, p)*GetCross(p3, p4, p) >= 0 && GetCross(p2, p3, p)*GetCross(p4, p1, p) >= 0
}

// 根据中心点和旋转角度，获取指定点旋转后的坐标
func GetPPosByRadians(center, p0 Vector3, forward Vector3) Vector3 {
	pp0 := p0.Sub(center)

	return Vector3{
		Vector3: protoMsg.Vector3{
			X: pp0.X*forward.Z + pp0.Z*forward.X + center.X,
			Y: 0,
			Z: -pp0.X*forward.X + pp0.Z*forward.Z + center.Z,
		},
	}
}

// 根据中心点和长宽和旋转角度，确定新的4个角坐标
func GetSquare4PPos(center Vector3, length, width int, forward Vector3) (p1, p2, p3, p4 Vector3) {
	minX := center.X - float32(length/2)
	minZ := center.Z - float32(width/2)
	maxX := center.X + float32(length/2)
	maxZ := center.Z + float32(width/2)

	pp1 := NewVector3(minX, 0, minZ)
	pp2 := NewVector3(minX, 0, maxZ)
	pp3 := NewVector3(maxX, 0, maxZ)
	pp4 := NewVector3(maxX, 0, minZ)

	p1 = GetPPosByRadians(center, pp1, forward)
	p2 = GetPPosByRadians(center, pp2, forward)
	p3 = GetPPosByRadians(center, pp3, forward)
	p4 = GetPPosByRadians(center, pp4, forward)
	return
}

// 根据起始点，距离，角度，获取目标坐标
func GetTarPosByRadians(start Vector3, radius float64, radians float64) (tar Vector3) {
	tar.X = start.X + float32(math.Cos(radians)*radius)
	tar.Z = start.Z + float32(math.Sin(radians)*radius)
	tar.Y = start.Y
	return
}

// 根据距离，角度，获取目标向量
func GetDirByRadians(radius float64, radians float64) (tar Vector3) {
	tar.X = float32(math.Cos(radians) * radius)
	tar.Z = float32(math.Sin(radians) * radius)
	return
}

// PosClient2Srv 客户端位置转换成服务器位置
// 服务器Z指天，客户端Y指天
func PosClient2Srv(cPos Vector3) (sPos Vector3) {
	sPos.X = cPos.X
	sPos.Y = cPos.Z
	sPos.Z = cPos.Y
	return
}

// PosSrv2Client 服务器位置转换成客户端位置
func PosSrv2Client(sPos Vector3) (cPos Vector3) {
	cPos.X = sPos.X
	cPos.Y = sPos.Z
	cPos.Z = sPos.Y
	return
}

// PosSrv2Nav 服务器位置转换成导航网格位置
func PosSrv2Nav(sPos Vector3) (nPos Vector3) {
	nPos.X = (sPos.X) //* -0.01
	nPos.Y = (sPos.Y) //* 0.01
	nPos.Z = (sPos.Z) //* -0.01
	return
}

// PosSrv2Nav 导航网格位置转换成服务器位置
func PosNav2Srv(nPos Vector3) (sPos Vector3) {
	sPos.X = nPos.X //* -100
	sPos.Y = nPos.Y //* 100
	sPos.Z = nPos.Z //* -100
	return
}

// PosClient2Nav 客户端位置转换成导航网格位置
func PosClient2Nav(cPos *Vector3) (nPos Vector3) {
	nPos.X = cPos.X //* -0.01
	nPos.Y = cPos.Z //* 0.01
	nPos.Z = cPos.Y //* -0.01
	return
}

// PosNav2Client 导航网格位置转换成客户端位置
func PosNav2Client(nPos Vector3) (cPos Vector3) {
	cPos.X = nPos.X //* -100
	cPos.Y = nPos.Z //* -100
	cPos.Z = nPos.Y //* 100
	return
}

// 客户端朝向转服务器朝向（U3D项目）
func ClientRota2Srv(crota int32) (sRota int32) {
	rot := 360 - crota - 270
	if rot > 360 {
		rot = rot - 360
	} else if rot < 0 {
		rot = 360 + rot
	}
	return rot
}

// Client2NavScale 将客户端尺寸转换成导航网格尺寸，用于做动态阻挡尺寸转换
func Client2NavScale(rawSize float32) float32 {
	return rawSize // / 100.0
}

// GetSign 计算符号
func GetSign(diff float64) (sign float64) {
	// abs := math.Abs(diff)
	if diff > 0.0 {
		sign = 1.0
	} else if diff < 0.0 {
		sign = -1.0
	} else {
		sign = 0
	}
	return
}

// WindRelativeAnglesDegrees
// 给定角度0，角度1（两个角度都是绝对角度数值，角度可以是任意未被归一成±180°），
// 返回角度0转向角度1之间夹角，值域为 ±180°
// 应用于怪物转向时候，需要计算角度差。比如：朝向是0°，需要转向到-179°，函数将会计算出-179°，而非181°
//
// Given two angles in degrees, 'wind' the rotation in Angle1 so that it avoids >180 degree flips.
// 参考网址：
// https://docs.unrealengine.com/en-US/API/Runtime/Core/Math/FMath/WindRelativeAnglesDegrees/index.html
// https://github.com/suijingfeng/VulkanTutorials/blob/master/Engine/Monkey/Math/Math.cpp
//
// 《3D数学基础（第2版）》-第八章-P215
// 将插值方程中使用的角度之间的差异限制在(-180°,+180°]区间以内找到最短的弧。公式如下：
//
// wrapPi(x)=x-360°floor((x+180°)/360°)
func WindRelativeAnglesDegrees(angle0, angle1 float64) (retAngle float64) {
	diff := angle0 - angle1
	abs := math.Abs(diff)
	retAngle = angle1
	if abs > 180.0 {
		retAngle += 360.0 * GetSign(diff) * math.Floor((abs/360.0)+0.5)
	}
	return
}

// 将度数限制在±180°
func UnwindDegrees(A float64) (R float64) {
	for A > 180.0 {
		A -= 360.0
	}
	for A < -180.0 {
		A += 360.0
	}
	R = A
	return
}

// CheckNavMeshTooFarAway 怪物在寻路到达目标点时，如果寻路路径长度大于与目标的直线距离*2时，视为目标点不可寻路
func CheckNavMeshTooFarAway(srcPos, dstPos Vector3, path []Vector3) bool {
	srcPos.SubS(dstPos)
	srcPos.Y = 0
	checkDst := srcPos.Dot(srcPos) * 2.0

	count := len(path)
	if count < 2 {
		return true
	}

	var tmpPos Vector3
	tmpPos = path[0]
	var totalDst float32
	totalDst = 0.0
	for i := 1; i < count; i++ {
		t := tmpPos.Sub(path[i])
		t.Y = 0
		totalDst += t.Dot(t)
		if totalDst > checkDst {
			return false
		}
		tmpPos = path[i]
	}

	return true
}

// > 计算两点距离平方
func CalPosLen(pos1, pos2 Vector3) float32 {
	return (pos1.X-pos2.X)*(pos1.X-pos2.X) + (pos1.Z-pos2.Z)*(pos1.Z-pos2.Z)
}

func CalPosLenNoZ(pos1, pos2 Vector3) float32 {
	return (pos1.X-pos2.X)*(pos1.X-pos2.X) + (pos1.Y-pos2.Y)*(pos1.Y-pos2.Y)
}

// > 叉积计算 > 0 P在Q顺时针 < 0 P在Q逆时针  == 0共线
func CalVectorMul(pos1, pos2 Vector3) float32 {
	return pos1.X*pos2.Z - pos2.X*pos1.Z
}

func CalCrossThree(pos1, pos2, pos Vector3) float32 {
	return (pos2.X-pos1.X)*(pos.Z-pos1.Z) - (pos.X-pos1.X)*(pos2.Z-pos1.Z)
}

// 点乘∣a∣∗∣b∣∗cosθ
func dotProduct(pos1, pos2 Vector3) float32 {
	return (pos1.X*pos2.X + pos1.Y*pos2.Y)
}

// > 计算扇形 和 圆形 是否相交
// > 输入role的pos ，角度，len 和 mon 的pos , r
func CheckSecCirInter(RolePos Vector3, angle float32, len float32, MonPos Vector3, r float32) bool {
	var MaxLen float32 = (len + r) * (len + r)
	if MaxLen < CalPosLen(RolePos, MonPos) {
		return false
	}

	forward := Rota2Vector(int32(angle + 90))
	MonPos = GetPPosByRadians(RolePos, MonPos, forward)

	MonPos.X -= RolePos.X
	MonPos.Z -= RolePos.Z
	RolePos.X = 0
	RolePos.Z = 0
	var RightPos Vector3 = GetTarPosByRadians(RolePos, float64(len), float64(AngleToRadian(90-angle)))
	var LeftPos Vector3 = NewVector3(-RightPos.X, RightPos.Y, RightPos.Z)

	//> 判断向量 monpos 是否 在 向量 left 和 right  之间
	//	fmt.Println(CalVectorMul(LeftPos, MonPos), "   ", CalVectorMul(LeftPos, RightPos), "  ", CalVectorMul(RightPos, MonPos), "  ", CalVectorMul(RightPos, LeftPos))
	if CalVectorMul(LeftPos, MonPos)*CalVectorMul(LeftPos, RightPos) >= 0 && CalVectorMul(RightPos, MonPos)*CalVectorMul(RightPos, LeftPos) >= 0 {
		return true
	}
	//> 圆是否和边相交
	//> 端点在圆内
	var MonLen float32 = float32(r * r)
	if CalPosLen(MonPos, LeftPos) <= MonLen || CalPosLen(MonPos, RightPos) <= MonLen || CalPosLen(MonPos, RolePos) <= MonLen {
		return true
	}

	///> 端点不在圆内
	if CheckLineCirInter(LeftPos, RolePos, MonPos, r) || CheckLineCirInter(RightPos, RolePos, MonPos, r) {
		return true
	}
	return false
}

//> 计算线段 和圆是否相交

func CheckLineCirInter(LeftPos, RolePos, MonPos Vector3, r float32) bool {
	var vec_r2l Vector3 = NewVector3(LeftPos.X-RolePos.X, LeftPos.Y-RolePos.Y, LeftPos.Z-RolePos.Z)
	var vec_l2r Vector3 = NewVector3(RolePos.X-LeftPos.X, RolePos.Y-LeftPos.Y, RolePos.Z-LeftPos.Z)
	var vec_l2m Vector3 = NewVector3(MonPos.X-LeftPos.X, MonPos.Y-LeftPos.Y, MonPos.Z-LeftPos.Z)
	var vec_r2m Vector3 = NewVector3(MonPos.X-RolePos.X, MonPos.Y-RolePos.Y, MonPos.Z-RolePos.Z)
	fenmu := float32(math.Abs(float64(CalVectorMul(vec_r2l, vec_r2m))))
	var llen float32 = fenmu * fenmu / CalPosLen(LeftPos, RolePos)
	///> 比较三角形面积
	if llen > float32(r*r) {
		return false
	}
	///> 两个角要为锐角
	if dotProduct(vec_l2m, vec_l2r) > float32(0) && dotProduct(vec_r2l, vec_r2m) > float32(0) {
		return true
	}
	return false
}

// > 计算矩形 和 圆形 是否相交
// > 输入中心点位置，长，宽， 和 mon 的pos ,r
func CheckRecCirInter(Center Vector3, length, width float32, MonPos Vector3, r float32, radio float64) bool {

	forward := Rota2Vector(int32(radio + 90))
	MonPos = GetPPosByRadians(Center, MonPos, forward)

	MonPos.X -= Center.X
	MonPos.Z -= Center.Z
	Center.X = 0
	Center.Z = 0
	maxX := Center.X + length/2
	maxZ := Center.Z + width/2
	//> 得到右上这个区间

	ppRU := NewVector3(maxX, 0, maxZ)

	//> 映射到第一区间
	MonPos.X = float32(math.Abs(float64(MonPos.X)))
	MonPos.Z = float32(math.Abs(float64(MonPos.Z)))

	var v Vector2 = Vector2{MonPos.X - ppRU.X, MonPos.Z - ppRU.Z}
	if v.X < 0 {
		v.X = 0
	}
	if v.Y < 0 {
		v.Y = 0
	}
	fmt.Println(v, "   ", v.X*v.X+v.Y*v.Y, "  ", r*r)
	if v.X*v.X+v.Y*v.Y <= r*r {
		return true
	}
	return false
}

/**
*	计算线段与圆的交点
*	传参：圆心，圆半径，起点，终点
*	返回：是否有交点
 */
func BetweenLineAndCircle2Num(circleCenter Vector3, circleRadius float32, point1, point2 Vector3) bool {
	// fmt.Println("p1:", point1)
	// fmt.Println("p2:", point2)
	// fmt.Println("p:", circleCenter)
	var dx = point2.X - point1.X
	// fmt.Println("dx:", dx)
	var dy = point2.Z - point1.Z
	// fmt.Println("dy:", dy)
	var a = dx*dx + dy*dy
	// fmt.Println("a:", a)
	var b = 2 * (dx*(point1.X-circleCenter.X) + dy*(point1.Z-circleCenter.Z))
	// fmt.Println("b:", b)
	var c = (point1.X-circleCenter.X)*(point1.X-circleCenter.X) + (point1.Z-circleCenter.Z)*(point1.Z-circleCenter.Z) - circleRadius*circleRadius
	// fmt.Println("c:", c)
	var determinate = b*b - 4*a*c
	if (a <= 0.0000001) || (determinate < -0.0000001) {
		return false
	}
	if determinate < 0.0000001 && determinate > -0.0000001 {
		//⼀个交点的情况
		t := -b / (2 * a)
		intersection1 := NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
		a := point1.Sub(intersection1)
		// a.Normalize()
		b := point2.Sub(intersection1)
		// b.Normalize()
		return !isVectorRotaEqual(a, b)

		// if a.IsEqualXZ(b) {
		// 	return false
		// } else {
		// 	return true
		// }
	} else {
		tmp := float32(math.Sqrt(float64(determinate)))
		t := ((-b + tmp) / (2 * a))
		{
			intersection1 := NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
			a := point1.Sub(intersection1)
			// a.Normalize()
			b := point2.Sub(intersection1)
			// b.Normalize()
			if !isVectorRotaEqual(a, b) {
				return true
			}
		}
		{
			t = ((-b - tmp) / (2 * a))
			intersection1 := NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
			a := point1.Sub(intersection1)
			a.Normalize()
			b := point2.Sub(intersection1)
			b.Normalize()
			if !isVectorRotaEqual(a, b) {
				return true
			}
		}
		return false
	}
}

/*
判断一条线上的二个向量是不是同方向
*/
func isVectorRotaEqual(a, b Vector3) bool {
	var isx, isz bool
	if (a.X >= 0 && b.X >= 0) || (a.X < 0 && b.X < 0) {
		isx = true
	}
	if (a.Z >= 0 && b.Z >= 0) || (a.Z < 0 && b.Z < 0) {
		isz = true
	}
	return isx && isz
	// if a.Z != 0 && b.Z != 0 &&
	// 	a.X != 0 && b.X != 0 {
	// 	if a.X/a.Z == b.X/b.Z {
	// 		return true
	// 	} else {
	// 		return false
	// 	}
	// }
	// else if a.X != 0 && b.X != 0 {
	// 	if a.Z/a.X == b.Z/b.X {
	// 		return true
	// 	} else {
	// 		return false
	// 	}
	// } else {
	// 	return false
	// }
}

/**
*	计算线段与圆的交点
*	传参：圆心，圆半径，起点，终点
*	返回：交点1，交点2
 */
func BetweenLineAndCircle(circleCenter Vector3, circleRadius float32, point1, point2 Vector3) (inerpointnum int32, intersection1, intersection2 Vector3) {
	//由于射线检测是不会赋Y轴的，所以这里需要在return的时候修改下Y轴
	defer func() {
		intersection1.Y = point1.Y
		intersection2.Y = point1.Y
	}()

	var t float32 = 0
	var dx = point2.X - point1.X
	var dy = point2.Z - point1.Z
	var a = dx*dx + dy*dy
	var b = 2 * (dx*(point1.X-circleCenter.X) + dy*(point1.Z-circleCenter.Z))
	var c = (point1.X-circleCenter.X)*(point1.X-circleCenter.X) + (point1.Z-circleCenter.Z)*(point1.Z-circleCenter.Z) - circleRadius*circleRadius
	var determinate = b*b - 4*a*c
	if (a <= 0.0000001) || (determinate < -0.0000001) {
		//没有交点的情况
		intersection1 = *Vector3_Zero()
		intersection2 = *Vector3_Zero()
		return
	}
	if determinate < 0.0000001 && determinate > -0.0000001 {
		//⼀个交点的情况
		t = -b / (2 * a)
		intersection1 = NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
		intersection2 = *Vector3_Zero()
		inerpointnum = 1
		return
	}

	//两个交点的情况
	tmp := float32(math.Sqrt(float64(determinate)))
	t = ((-b + tmp) / (2 * a))
	intersection1 = NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
	t = ((-b - tmp) / (2 * a))
	intersection2 = NewVector3(point1.X+t*dx, 0, point1.Z+t*dy)
	inerpointnum = 2
	return
}

// 检查线条和圆的碰撞点
// 返回交点数量 和 交点
func CalcLineCircleCollision(srcPos, stpos, bossPos Vector3, bossRadius float32, offest Vector2, isDash bool) (int32, []Vector3) {
	bossPos.X += offest.X
	bossPos.Z += offest.Y
	var allPos []Vector3
	//> 如果初始点在圈中，需要判断位移向量和 起始点与怪物向量叉积,小于90度则代表朝里，则不运动，否则则直接终点
	if CalPosLen(srcPos, bossPos) <= bossRadius*bossRadius {
		vea := stpos.Sub(srcPos)
		veb := bossPos.Sub(srcPos)
		if vea.Dot2(veb) > 0 {
			allPos = append(allPos, srcPos)
			return 1, allPos
		} else {
			allPos = append(allPos, stpos)
			return 1, allPos
		}
	}
	dx := stpos.X - srcPos.X
	dy := stpos.Z - srcPos.Z
	Len := dx*dx + dy*dy
	b := 2 * (dx*(srcPos.X-bossPos.X) + dy*(srcPos.Z-bossPos.Z))
	c := (srcPos.X-bossPos.X)*(srcPos.X-bossPos.X) + (srcPos.Z-bossPos.Z)*(srcPos.Z-bossPos.Z) - bossRadius*bossRadius
	determinate := b*b - 4*Len*c
	if (Len <= 0.0000001) || (determinate < -0.0000001) {
		// No real solutions.
		allPos = append(allPos, stpos)
		return 0, allPos
	}
	if determinate < 0.0000001 && determinate > -0.0000001 {
		// One solution.
		t := -b / (2 * Len)
		add := NewVector3(t*dx, 0, t*dy)
		allPos = append(allPos, srcPos.Add(add))
		return 1, allPos
	}

	t := ((-b + float32(math.Sqrt(float64(determinate)))) / (2 * Len))
	add1 := NewVector3(t*dx, 0, t*dy)

	t = ((-b - float32(math.Sqrt(float64(determinate)))) / (2 * Len))
	add2 := NewVector3(t*dx, 0, t*dy)

	//> 判断是否同向
	SrToEn := stpos.Sub(srcPos)
	if SrToEn.Dot2(add1) > 0 {
		if CalPosLen(srcPos, stpos) > CalPosLen(srcPos, srcPos.Add(add1)) {
			allPos = append(allPos, srcPos.Add(add1))
		}

	}
	if SrToEn.Dot2(add2) > 0 {
		if CalPosLen(srcPos, stpos) > CalPosLen(srcPos, srcPos.Add(add2)) {
			allPos = append(allPos, srcPos.Add(add2))
		}
	}
	return int32(len(allPos)), allPos

}

// 检查某个点是否在背后
// dstPos 原始对象位置
// forwardAngle 原始对象朝向
// angleOff 正面的夹角半角
// checkPos 检查者位置
func CalcPosBehind(dstPos, checkPos Vector3, forwardAngle, angleOff int32) bool {
	// 两点重合了，将直接判断为正面
	if dstPos.IsEqualXZ(checkPos) {
		return false
	}
	checkTodst := VectorToAngle(dstPos.Sub(checkPos))
	return math.Abs(float64(checkTodst)-float64(forwardAngle)) < float64(angleOff)
}

// 移动结算碰撞相关函数
type RushPos struct {
	Start    Vector3
	End      Vector3
	StartLen float32
	EndLen   float32
}

type rushposList []*RushPos

func (p rushposList) Len() int { return len(p) }
func (p rushposList) Less(i, j int) bool {
	if p[i].StartLen < p[j].StartLen {
		return true
	} else {
		return p[i].EndLen < p[j].EndLen
	}
}
func (p rushposList) Swap(i, j int) {
	var temp *RushPos = p[i]
	p[i] = p[j]
	p[j] = temp
}

// > 判断位移过程中与某个Tinty的碰撞 参数：起始点，终点，目标，检测半径
func CheckTintyObstacles(stPos, edPos, tarpos Vector3, checkRadious float32) *RushPos {
	// 检查线条和圆的碰撞点 返回交点数量 和 交点
	num, poss := CalcLineCircleCollision(stPos, edPos, tarpos, checkRadious, Vector2_Zero(), false)
	data := &RushPos{}
	if num == 0 {
		//无交点，直接返回
		return nil
	} else if num == 1 {
		//在圆内
		data.Start = poss[0]
		data.StartLen = CalPosLen(stPos, poss[0])

	} else if num == 2 {
		//穿过了圆
		len1 := CalPosLen(stPos, poss[0])
		len2 := CalPosLen(stPos, poss[1])
		if len1 > len2 {
			len1, len2 = len2, len1
			poss[0], poss[1] = poss[1], poss[0]
		}
		data.Start = poss[0]
		data.End = poss[1]
		data.StartLen = len1
		data.EndLen = len2
	}
	return data
}

const (
	//忽略模型半径
	CHECKMODEL_1 = iota
	//不忽略模型半径（根据起始点选择最近的交点）
	CHECKMODEL_2
	//不忽略模型半径（根据Boss圆点选择最近交点）
	CHECKMODEL_3
)

const (
	CheckMove_Dash  = 1
	CheckMove_Rush  = 2
	CheckMove_Flash = 3
)

/**
*	返回点位移的终点
*	参数：起点，终点，目标点（圆心），检查半径和
* 	返回值：最终点
 */
func GetPosWithCircle(StartPos, FinallyPos, CenterPos Vector3, checkRadious float32, CheckModel, CheckMove int32) Vector3 {

	//检查模型类型
	switch CheckMove {
	case CheckMove_Dash:
		//起点与圆心的距离
		r_StartPos_Center := CalPosLen(StartPos, CenterPos)
		//起点是否在圆内
		isStartPosInCircle := (r_StartPos_Center <= checkRadious*checkRadious)
		//都不穿
		if isStartPosInCircle {
			//若起点在圆内，直接返回起点
			return StartPos
		} else {
			// 交点数量， 交点1， 交点2
			interNum, interP1, interP2 := BetweenLineAndCircle(CenterPos, checkRadious, StartPos, FinallyPos)
			//若起点不在圆内
			if interNum > 0 {
				// 交点1与起点的距离
				r_interP1_SatrtPos := CalPosLen(StartPos, interP1)
				// 交点2与起点的距离
				r_interP2_SatrtPos := CalPosLen(StartPos, interP2)
				// 终点与起点的距离
				r_FinPos_StartPos := CalPosLen(StartPos, FinallyPos)
				//如果有2交点,返回距离起点更近的交点
				if r_interP1_SatrtPos >= r_interP2_SatrtPos && r_FinPos_StartPos >= r_interP2_SatrtPos {
					return interP2
				} else if r_interP2_SatrtPos >= r_FinPos_StartPos && r_interP1_SatrtPos >= r_FinPos_StartPos {
					return FinallyPos
				} else {
					return interP1
				}
			}
		}

	case CheckMove_Rush, CheckMove_Flash:
		// 终点与圆心的距离
		r_FinPos_Center := CalPosLen(FinallyPos, CenterPos)
		//终点是否在圆内
		isFinPosInCircle := (r_FinPos_Center <= checkRadious*checkRadious)
		//不穿墙,穿墙且穿怪
		if isFinPosInCircle {
			if CheckModel == CHECKMODEL_2 {
				// 交点数量， 交点1， 交点2
				interNum, interP1, interP2 := BetweenLineAndCircle(CenterPos, checkRadious, StartPos, FinallyPos)
				if interNum == 0 {
					//没有交点，直接返回目标点左边的点（center.x-radious）推开
					return NewVector3(CenterPos.X-checkRadious, CenterPos.Y, CenterPos.Z)
				}
				// 交点1与起点的距离
				r_interP1_FinPos := CalPosLen(FinallyPos, interP1)
				// 交点2与起点的距离
				r_interP2_FinPos := CalPosLen(FinallyPos, interP2)
				//如果终点在圆内,返回距离终点更近的交点
				if r_interP1_FinPos > r_interP2_FinPos {
					return interP2
				} else {
					return interP1
				}
			} else {
				// 交点数量， 交点3， 交点4
				_, interP3, interP4 := BetweenLineAndCircle(CenterPos, checkRadious, CenterPos, FinallyPos)
				// 交点1与起点的距离
				r_interP3_FinPos := CalPosLen(FinallyPos, interP3)
				// 交点2与起点的距离
				r_interP4_FinPos := CalPosLen(FinallyPos, interP4)
				//如果终点在圆内,返回距离终点更近的交点
				if r_interP3_FinPos > r_interP4_FinPos {
					return interP4
				} else {
					return interP3
				}
			}

		}

	}

	return FinallyPos
}

/*
计算目标点在指定射线上的投影坐标点
*/
func CompShadow(cpos, tpos Vector3, rota int32) (result Vector3) {

	//目标向量
	result = tpos.Sub(cpos)
	//第二个弧度度
	brad := float32(math.Atan(float64(result.Z) / float64(result.X)))
	if result.X < 0 && brad < 0 {
		brad = brad + math.Pi
	}
	arad := AngleToRadian(float32(rota))

	// fmt.Println("cos:", math.Cos(float64(arad-brad)))
	l := float64(result.Len()) * math.Cos(float64(arad-brad))
	rotapos := Rota2Vector(rota)
	rotapos.MulS(float32(l))
	result = cpos.Add(rotapos)
	return
}

func GetShadow_New(cpos, tpos Vector3, rota int32) (result Vector3) {

	//直接以100为距离，模拟出一个坐标
	tmpPos := PosMove(cpos, rota, 1)
	//直线a=cpos,b=tmpPos 与 直线外一点tpos
	v := tmpPos.Sub(cpos)
	u := tpos.Sub(cpos)
	return cpos.Add(v.Mul(v.Dot(u) / v.Dot(v)))
}

// 计算两个圆的交点
// 返回值可能有0、1、2个交点(重合时只会有一个)
func IntersectTwoCircles(c1, c2 Vector3, r1, r2 float32) (result1 *Vector3, result2 *Vector3) {
	// 计算两个圆心之间的距离
	d := c1.Sub(c2).Len()

	// 如果两个圆心距离太远没有交点
	if d > r1+r2 {
		return
	}
	// 如果两个圆心距离正好相等是，没有交点
	if d == 0 && r1 == r2 {
		return
	}

	// 如果两个圆心距离太近只有一个交点
	if rl := math.Abs(float64(r1 - r2)); d < float32(rl) {

		result := NewVector3((c1.X+c2.X)/2, c1.Y, (c1.Z+c2.Z)/2)
		return &result, nil
	}

	// 计算出交点
	a := (r1*r1 - r2*r2 + d*d) / (2 * d)
	h := float32(math.Sqrt(float64(r1*r1 - a*a)))
	x2 := c1.X + a*(c2.X-c1.X)/d
	z2 := c1.Z + a*(c2.Z-c1.Z)/d
	x3_1 := x2 + h*(c2.Z-c1.Z)/d
	z3_1 := z2 - h*(c2.X-c1.X)/d
	x3_2 := x2 - h*(c2.Z-c1.Z)/d
	z3_2 := z2 + h*(c2.X-c1.X)/d

	// 生成包含交点的点向量数组
	if x3_1 == x3_2 && z3_1 == z3_2 {
		result := NewVector3(float32(x3_1), c1.Y, float32(z3_1))
		return &result, nil
	}
	result := NewVector3(float32(x3_1), c1.Y, float32(z3_1))
	result1 = &result
	result = NewVector3(float32(x3_2), c1.Y, float32(z3_2))
	result2 = &result
	return
}
